package com.tianji.learning.task;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.tianji.common.utils.CollUtils;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.context.TableInfoContext;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.service.IPointsBoardSeasonService;
import com.tianji.learning.service.IPointsBoardService;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;

import static com.tianji.learning.constants.LearningConstants.POINT_BOARD_TABLE;

/**
 * <p>
 * 任务类
 * </p>
 *
 * @author taohongrun
 * @since 2025/2/12
 */
@Component
@RequiredArgsConstructor
@Slf4j
public class PointsBoardPersistentTask {
    private final IPointsBoardSeasonService seasonService;

    private final IPointsBoardService pointsBoardService;

    private final StringRedisTemplate redisTemplate;

    //@Scheduled(cron = "0 0 3 1 * ?") // 每月1号，凌晨3点执行
    @XxlJob("createTableJob")
    public void createPointsBoardTableOfLastSeason(){
        log.info("createTableJob任务开始执行.......");
        // 1.获取上个月第一天时间
        LocalDate time = LocalDate.now().plusDays(-12L).plusDays(-1L).withDayOfMonth(1);
        log.info("createTableJob任务执行，上个月时间：{}", time);
        // 2.查询赛季id
        Integer season = seasonService.querySeasonByTime(time);
        log.info("createTableJob任务执行，赛季id：{}", season);
        // 3.创建表
        pointsBoardService.createPointsBoardTableBySeason(season);
        log.info("createTableJob任务执行，创建表成功");
        log.info("createTableJob任务执行结束.......");
    }

    @XxlJob("savePointsBoard2DB")
    public void savePointsBoard2DB(){
        log.info("savePointsBoard2DB任务开始执行.......");
        // 1.获取上月第一天时间
        // 获取上一个月的第一天
        LocalDate time = LocalDate.now().minusMonths(1).withDayOfMonth(1);
        log.info("savePointsBoard2DB任务执行，上个月时间：{}", time);
        // 2.计算动态表名
        // 2.1.查询赛季信息
        Integer season = seasonService.querySeasonByTime(time);
        log.info("savePointsBoard2DB任务执行，赛季id：{}", season);
        // 2.2.将表名存入ThreadLocal
        TableInfoContext.setInfo(POINT_BOARD_TABLE + season);
        log.info("savePointsBoard2DB任务执行，动态表名：{}存入本地线程", TableInfoContext.getInfo());
        // 3.查询榜单数据
        // 3.1.拼接KEY
        String key = StrUtil.format(RedisConstants.POINTS_BOARD_KEY ,DateUtil.format(time.atStartOfDay(), "yyyyMM"));
        log.info("savePointsBoard2DB任务执行，redis key：{}", key);
        // 3.2.查询数据
        int index = XxlJobHelper.getShardIndex();
        int total = XxlJobHelper.getShardTotal();
        log.info("savePointsBoard2DB任务执行，分片序号：{}，分片总数：{}", index, total);
        int pageNo = index + 1; // 起始页，就是分片序号+1
        int pageSize = 5;
        while (true) {
            List<PointsBoard> boardList = pointsBoardService.queryCurrentBoardList(key, pageNo, pageSize);
            log.info("savePointsBoard2DB任务执行，redis key：{}，数据：{}", key, boardList);
            if (boardList.isEmpty()) {
                break;
            }
            // 4.持久化到数据库
            pointsBoardService.saveBatch(boardList);
            log.info("savePointsBoard2DB任务执行，持久化到数据库成功");
            // 5.翻页
            pageNo+= total;
            log.info("savePointsBoard2DB任务执行，翻页，pageNo：{}", pageNo);
        }
        //任务结束，移除动态表明
        TableInfoContext.remove();
        log.info("savePointsBoard2DB任务执行，移除动态表名：{}", TableInfoContext.getInfo());

        //向redis中添加分片状态表示该分片已完成
        redisTemplate.opsForSet().add(getShardKey(total), String.valueOf(index));
        log.info("savePointsBoard2DB任务执行，向redis分片完成列表:{}中添加分片:{}", getShardKey(total),index);

        if(index +1 == total){
            while(true){
                boolean b = checkAllShardsCompleted(total);
                if (b) break;
            }
        }

        log.info("savePointsBoard2DB任务执行结束.......");
    }



    @XxlJob("clearPointsBoardFromRedis")
    public void clearPointsBoardFromRedis(){
        log.info("clearPointsBoardFromRedis任务开始执行.......");
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);
        log.info("clearPointsBoardFromRedis任务执行，上月时间：{}", time);
        // 2.计算key
        String key = StrUtil.format(RedisConstants.POINTS_BOARD_KEY, DateUtil.format(time, "yyyyMM"));
        log.info("clearPointsBoardFromRedis任务执行，redis key：{}", key);
        // 3.删除
        redisTemplate.unlink(key);
        log.info("clearPointsBoardFromRedis任务执行，redis key：{}，删除成功", key);
        log.info("clearPointsBoardFromRedis任务执行结束.......");
    }

    // 检查所有分片是否完成
    private boolean checkAllShardsCompleted(int total) {
        // 查询数据库/Redis中所有分片状态
        if(redisTemplate.opsForSet().size(getShardKey(total)).intValue() != total){
            log.info("clearPointsBoardFromRedis任务执行，分片未完成，继续执行");
            return false;
        }
        redisTemplate.delete(getShardKey(total));
        // 如果全部完成，则触发任务clearPointsBoardFromRedis
        log.info("clearPointsBoardFromRedis任务执行，分片已完成，触发任务clearPointsBoardFromRedis");
        //XxlJobHelper.trigger(clearPointsBoardFromRedis);
        return true;
    }

    private String getShardKey(int total) {
        return "shard:savePointsBoard2DB:"+ total;
    }
}
